/* This file is part of MyRename
 * Copyright (C) 2005-2009 Kai-Chieh Ku (kjackie@gmail.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "stdafx.h"
#include "undofile.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

char CUndoFile::identifier[2] = {'U', 'D'};

CUndoFile::CUndoFile()
	:fn(NULL), fh(NULL), workdir(NULL), _mode(0)
{
}

CUndoFile::CUndoFile(LPCTSTR filename, uint16 mode)
	:fn(NULL), fh(NULL), workdir(NULL), _mode(0)
{
	open(filename, mode);
}

CUndoFile::~CUndoFile()
{
	if (_mode & UNDO_WRITE)
		WriteFooter();
	close();
}

bool CUndoFile::open(LPCTSTR filename, uint16 mode)
{
	if (filename == NULL || fh != NULL)
		return false;

	_mode = mode;
	TCHAR strmode[4] = {0};

	if (_mode & UNDO_WRITE)
		strmode[0] = _T('w');
	else
		strmode[0] = _T('r');

	strmode[1] = _T('b');

	if ((fh = _tfopen(filename, strmode)) == NULL)
		return false;

	fn = new TCHAR [_tcslen(filename) + 1];
	_tcscpy(fn, filename);

	if (_mode & UNDO_WRITE) {
		if (! WriteHeader()) {
			close();
			return false;
		}
		return true;
	}

	fread((void *) &header, sizeof(UndoFileHeader), 1, fh);

	if (! verify()) {
		close();
		return false;
	}

	return true;
}

bool CUndoFile::InitRead()
{
	if (_mode & UNDO_WRITE || fh == NULL)
		return false;
	fseek(fh, header.offset, SEEK_SET);
	TCHAR temp[_MAX_PATH];
	if (bfgets(temp, _MAX_PATH, fh) == NULL)
		return false;
	size_t len = _tcslen(temp);
	workdir = new TCHAR [len + 1];
	_tcscpy(workdir, temp);

	return true;
}

int CUndoFile::ReadSession(CString &before, CString &after)
{
	if (_mode & UNDO_WRITE || fh == NULL)
		return READ_FAIL;
	if (feof(fh))
		return READ_EOF;

	TCHAR buf[_MAX_FNAME + _MAX_EXT];
	LPTSTR res;

	res = bfgets(buf, _MAX_FNAME + _MAX_EXT, fh);
	if (res == NULL) {
		if (feof(fh)) return READ_EOF;
		return READ_FAIL;
	}
	before = buf;

	res = bfgets(buf, _MAX_FNAME + _MAX_EXT, fh);
	if (res == NULL) {
		if (feof(fh)) return READ_EOF;
		return READ_FAIL;
	}
	after = buf;

	return READ_OK;
}

bool CUndoFile::InitWrite(LPCTSTR dir)
{
	if (!(_mode & UNDO_WRITE) || fh == NULL)
		return false;
	_fputts(dir, fh);
	bfputc(_T('\0'), fh);
	return true;
}

bool CUndoFile::WriteSession(LPCTSTR before, LPCTSTR after)
{
	if (!(_mode & UNDO_WRITE) || fh == NULL)
		return false;

	if (_fputts(before, fh) == _TEOF)
		return false;
	bfputc(_T('\0'), fh);
	if (_fputts(after, fh) == _TEOF)
		return false;
	bfputc(_T('\0'), fh);
	return true;
}

void CUndoFile::close()
{
	if (fn != NULL)
		delete [] fn;
	if (workdir != NULL)
		delete [] workdir;
	if (fh != NULL)
		fclose(fh);
	fn = NULL;
	workdir = NULL;
	fh = NULL;
}

bool CUndoFile::WriteHeader()
{
	if (!(_mode & UNDO_WRITE) || fh == NULL)
		return false;
	rewind(fh);
	header.identifier[0] = identifier[0];
	header.identifier[1] = identifier[1];
	header.version = UNDO_VER;
	header.offset = sizeof(UndoFileHeader);
	return fwrite((void *) &header, sizeof(UndoFileHeader), 1, fh) == 1;
}

bool CUndoFile::WriteFooter()
{
	if (!(_mode & UNDO_WRITE) || fh == NULL)
		return false;
	if (fwrite((void *) identifier, sizeof(char), 2, fh) == 2) {
		close();
		return true;
	}
	return false;
}

bool CUndoFile::verify()
{
	if (_mode & UNDO_WRITE || fh == NULL)
		return false;
	if (header.identifier[0] != identifier[0] || header.identifier[1] != identifier[1])
		return false;
	fseek(fh, -2, SEEK_END);
	char buf[2];
	fread((void *) buf, sizeof(char), 2, fh);
	if (buf[0] != identifier[0] || buf[1] != identifier[1])
		return false;
	return true;
}

LPCTSTR CUndoFile::GetFilename() const
{
	return fn;
}

LPCTSTR CUndoFile::GetWorkPath() const
{
	return workdir;
}

LPTSTR CUndoFile::bfgets(LPTSTR buf, int len, FILE *stream)
{
	TCHAR ch;
	int readed = 0;
	while (! feof(stream) && readed < len) {
		if (fread(&ch, sizeof(TCHAR), 1, stream)) {
			buf[readed] = ch;
			if (ch == _T('\0'))
				break;
			readed++;
		}
		else return NULL;
	}

	return buf;
}

int CUndoFile::bfputc(TCHAR ch, FILE *stream)
{
	if (fwrite(&ch, sizeof(TCHAR), 1, stream) == 1)
		return ch;
	return _TEOF;
}
